#include "main.h"
#include "netpacket.h"
#include "parameters.h"
#include "string.h"
#include "stdio.h"
#include "err.h"
#include "FreeRTOS.h"
#include "task.h"
#include "api.h"
#include "tcp.h"
#include "netconf.h"
#include "stm32f4xx_eth.h"
#include "stm32f4xx_eth_bsp.h"
#include "gsm.h"

sNetStruct netStruct;
static struct tcp_pcb *pcb;
static struct ip_addr ip_dest;

#define ETERNET_MAX_PACKET_LENGTH	1024

const char header[] 			= {"ROIL"};
const char net_use_ethernet[]	= {"NE"};
const char net_use_gsm[] 		= {"NG"};
const char net_end_capion[] 	= {"END"};

#define DHCP_TASK_PRIO  		( tskIDLE_PRIORITY + 1 )
#define NET_TASK_PRIO			( tskIDLE_PRIORITY + 2 )
#define NET_TASK_STACK_SIZE		512
#define DHCP_TASK_SIZE			( configMINIMAL_STACK_SIZE * 2 )

void NetPacketInsertHeader(void);
void NetPacketInsertSystemFields(void);

void NetPacketInsertSystemFields(void);
eReturn NetGSM_up(void);
eReturn NetworkSendGSM(void);
eReturn NetworkSendEthernet(void);
eReturn NetworkConnectUpEthernet(void);
eReturn NetworkConnectDownGSM(void);
eReturn NetworkConnectDownEthernet(void);
eReturn NetworkConnectDownGSM(void);
eReturn NetworkConnectUpGSM(void);

void NetWorkTask(void *pvParameters)
{
	/* used Ethernet - init */
	if((startParam.net.net_use & E_NETWORK_USE_ETHERNET) || (startParam.net.net_use & E_NETWORK_USE_PRIORITY_ETHERNET) ||
			(startParam.net.net_use & E_NETWORK_USE_PRIORITY_GSM))
	{
		netStruct.tune_action = E_NET_STATE_INIT;
		netStruct.connect_state = E_NET_STATE_INIT;

		ETH_BSP_Config();
		if(EthStatus & ETH_LINK_FLAG)
		{
			LwIP_Init();
			if(startParam.net.net_use & E_NETWORK_USE_DHCP)
			{
				xTaskCreate(LwIP_DHCP_task, 0, DHCP_TASK_SIZE, NULL, tskIDLE_PRIORITY, NULL);
				while(( DHCP_state == DHCP_START ) || ( DHCP_state == DHCP_WAIT_ADDRESS ))
					vTaskDelay(100);
				if(( DHCP_state != DHCP_ADDRESS_ASSIGNED ) || ( DHCP_state == DHCP_TIMEOUT ))
					startParam.net.net_use = E_NETWORK_USE_GSM;
				if(DHCP_state == DHCP_ADDRESS_ASSIGNED)
					BuzzerON_Once();
			}
			else
			{
				if((startParam.net.ip_addr[0] == 0) && (startParam.net.ip_addr[1] == 0)
						&& (startParam.net.ip_addr[3] == 0) && (startParam.net.ip_addr[4] == 0))
				{
					startParam.net.net_use = E_NETWORK_USE_GSM;
				}
			}
		}
		else
			startParam.net.net_use = E_NETWORK_USE_GSM;
	}

	if((startParam.net.net_use & E_NETWORK_USE_GSM) || (startParam.net.net_use & E_NETWORK_USE_PRIORITY_ETHERNET) ||
			(startParam.net.net_use & E_NETWORK_USE_PRIORITY_GSM))
	{
		xTaskCreate(GSM_Task, 0, 768, NULL, tskIDLE_PRIORITY+1, NULL);
		while(gsm.state != E_GSM_OK)
			vTaskDelay(100);
		GSM_Init();
	}

	/* start modbus */
	ModbusInit();

	for(;;)
	{
		/********************************/
		/* connect Up 					*/
		/********************************/
		if(netStruct.tune_action & E_NET_STATE_TUNE_CONNECT_UP)
		{
			/* use only ethernet */
			if(startParam.net.net_use & E_NETWORK_USE_ETHERNET)
			{
				/* connect error */
				if(NetworkConnectUpEthernet() == E_OK)
				{
					netStruct.connect_state = E_NET_STATE_CONNECT_UP;
					netStruct.type_connection = E_NETWORK_USE_ETHERNET;
				}
				else
					netStruct.connect_state = E_NET_STATE_CONNECT_ERR;
			}

			/* priority ethernet */
			if(startParam.net.net_use & E_NETWORK_USE_PRIORITY_ETHERNET)
			{
				if(NetworkConnectUpEthernet() == E_OK)
				{
					netStruct.connect_state = E_NET_STATE_CONNECT_UP;
					netStruct.type_connection = E_NETWORK_USE_ETHERNET;
				}
				else
				{
					if(NetworkConnectUpGSM() == E_OK)
					{
						netStruct.connect_state = E_NET_STATE_CONNECT_UP;
						netStruct.type_connection = E_NETWORK_USE_GSM;
					}
					else
					{
						netStruct.connect_state = E_NET_STATE_CONNECT_ERR;
						NVIC_SystemReset();
					}
				}
			}

			/* use gsm only */
			if(startParam.net.net_use & E_NETWORK_USE_GSM)
			{
				if(NetworkConnectUpGSM() == E_OK)
				{
					netStruct.connect_state = E_NET_STATE_CONNECT_UP;
					netStruct.type_connection = E_NETWORK_USE_GSM;
				}
				else
					netStruct.connect_state = E_NET_STATE_CONNECT_ERR;
			}

			/* priority gsm */
			if(startParam.net.net_use & E_NETWORK_USE_PRIORITY_GSM)
			{
				if(NetworkConnectUpGSM() == E_OK)
				{
					netStruct.connect_state = E_NET_STATE_CONNECT_UP;
					netStruct.type_connection = E_NETWORK_USE_GSM;
				}
				else
				{
					if(NetworkConnectUpEthernet() == E_OK)
					{
						netStruct.connect_state = E_NET_STATE_CONNECT_UP;
						netStruct.type_connection = E_NETWORK_USE_ETHERNET;
					}
					else
					{
						netStruct.connect_state = E_NET_STATE_CONNECT_ERR;
						NVIC_SystemReset();
					}
				}
			}

			if(netStruct.connect_state == E_NET_STATE_CONNECT_UP)
				NetPacketInsertHeader();

			netStruct.tune_action = 0;
		}

		/********************************/
		/* connect Down					*/
		/********************************/
		if(netStruct.tune_action == E_NET_STATE_TUNE_CONNECT_DOWN)
		{
			/* connect down */
			netStruct.connect_state = E_NET_STATE_CONNECT_DOWN;
			if(netStruct.type_connection == E_NETWORK_USE_ETHERNET)
				NetworkConnectDownEthernet();
			if(netStruct.type_connection == E_NETWORK_USE_GSM)
				NetworkConnectDownGSM();

			netStruct.tune_action = 0;
		}

		/********************************/
		/* Send Packet  				*/
		/********************************/
		if(netStruct.tune_action & E_NET_STATE_SEND_PACKET)
		{
			netStruct.connect_state = E_NET_STATE_SEND_PACKET;
			if(netStruct.type_connection == E_NETWORK_USE_ETHERNET)
			{
				if(NetworkSendEthernet() != E_OK)
					netStruct.tune_action = E_NET_STATE_TUNE_CONNECT_DOWN;
				else
					BuzzerON_Once();
			}
			if(netStruct.type_connection == E_NETWORK_USE_GSM)
			{
				if(NetworkSendGSM() != E_OK)
					netStruct.tune_action = E_NET_STATE_TUNE_CONNECT_DOWN;
				else
					BuzzerON_Once();
			}
			memset((char*)&netStruct.netPacket, 0, sizeof(netStruct.netPacket));
			netStruct.tune_action &=~ E_NET_STATE_SEND_PACKET;
		}
		vTaskDelay(1000);
	}
}

void NetPacketInsertHeader(void)
{
	memset(&netStruct.netPacket, 0, sizeof(netStruct.netPacket));
	strcpy((char*)&netStruct.netPacket, (char*)&header);
	NetPacketInsertSystemFields();
}

void NetPacketInsertSystemFields(void)
{
	uint16_t i;
	RTC_TimeTypeDef time;
	RTC_DateTypeDef date;
	HAL_RTC_GetTime(&RtcHandle, &time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&RtcHandle, &date, RTC_FORMAT_BIN);
	/**************************/
	/**		Clear Buffer	  */
	/**************************/
	strcpy((char*)&netStruct.netPacket, (char*)&header);
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '-';

	/**************************/
	/**	Insert ID and Version */
	/**************************/
	sprintf((char*)&netStruct.netPacket[i], "%x", startParam.id);
	// version firmware
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '-';
	sprintf((char*)&netStruct.netPacket[i], "%02x", (uint16_t)VERSION_SOFTWARE);

	/************************/
	/**	Insert Date-Time	*/
	/************************/
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '-';
	sprintf((char*)&netStruct.netPacket[i], "%x", date.Year);
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '.';
	sprintf((char*)&netStruct.netPacket[i], "%x", date.Month);
	// day
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '.';
	sprintf((char*)&netStruct.netPacket[i], "%x", date.Date);
	// hour
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '.';
	sprintf((char*)&netStruct.netPacket[i], "%x", time.Hours);
	// minutes
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '.';
	sprintf((char*)&netStruct.netPacket[i], "%x", time.Minutes);
	// seconds
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '.';
	sprintf((char*)&netStruct.netPacket[i], "%x", time.Seconds);
	i = strlen(netStruct.netPacket);
	netStruct.netPacket[i++] = '-';

	/*************************/
	/**	Used type Connection */
	/*************************/
	if(netStruct.type_connection & E_NETWORK_USE_ETHERNET)
	{
		i = strlen(netStruct.netPacket);
		strcpy((char*)&netStruct.netPacket[i], net_use_ethernet);
	}
	if(netStruct.type_connection & E_NETWORK_USE_GSM)
	{
		i = strlen(netStruct.netPacket);
		strcpy((char*)&netStruct.netPacket[i], net_use_gsm);
		i = strlen(netStruct.netPacket);
		sprintf((char*)&netStruct.netPacket[i], "%x", gsm.caption.rssi);
		i = strlen(netStruct.netPacket);
		netStruct.netPacket[i++] = '-';
	}

	/*************************/
	/**	Counter Packet 		 */
	/*************************/
	i = strlen(netStruct.netPacket);
	sprintf((char*)&netStruct.netPacket[i], "%x", netStruct.couneterPacket);
	i = strlen(netStruct.netPacket);

	/* end Header Packet*/
	netStruct.netPacket[i] = '!';
	netStruct.couneterPacket ++;
}

void NetworkEndPacket(void)
{
	uint16_t i;
	i = strlen((char*)netStruct.netPacket);
	strcpy((char*)&netStruct.netPacket[i], (char*)&net_end_capion);
}

/**********************/
/* Connect up network */
/**********************/
eReturn NetworkConnectUpEthernet(void)
{
	IP4_ADDR(&ip_dest, startParam.net.ip_dest[0], startParam.net.ip_dest[1], startParam.net.ip_dest[2], startParam.net.ip_dest[3]);
	pcb = tcp_new();
	tcp_bind(pcb, IP_ADDR_ANY, 3003);
	if(tcp_connect(pcb, &ip_dest, 3003, ERR_OK) != ERR_OK)
		return E_EROR;
	vTaskDelay(500);
	return E_OK;
}

eReturn NetworkConnectUpGSM(void)
{
	uint8_t attempt;

	if(!(HAL_GPIO_ReadPin(GSM_STATUS_PORT, GSM_STATUS_PIN)))
	{
		gsm.state = E_GSM_INIT;
		while(gsm.state == E_GSM_INIT)
			vTaskDelay(100);
	}

	for(attempt=0; attempt<GSM_MAX_ATTEMPT_GET_RSSI; attempt++)
	{
		GSM_get_RSSI();
		if(gsm.caption.rssi > GSM_MIN_RSSI_VALUE)
			break;
	}
	if(gsm.caption.rssi < GSM_MIN_RSSI_VALUE)
		return E_EROR;
	if(GSM_set_TCP_connect() == E_OK)
		return E_OK;
	return E_EROR;
}

/************************/
/* Connect Down network */
/************************/
eReturn NetworkConnectDownEthernet(void)
{
	vTaskDelay(500);
	tcp_close(pcb);
	return E_OK;
}

eReturn NetworkConnectDownGSM(void)
{
	GSM_TCP_disconnect();
	return E_OK;
}

/************************/
/* Network send packet  */
/************************/
eReturn NetworkSendEthernet(void)
{
	uint16_t i;
	uint16_t i2=0;
	i = strlen((char*)&netStruct.netPacket);
	if(i>ETERNET_MAX_PACKET_LENGTH)
	{
		i2 = 0;
		while(i2<ETERNET_MAX_PACKET_LENGTH)
		{
			vTaskDelay(1000);
			if(tcp_write(pcb, &netStruct.netPacket[i2], ETERNET_MAX_PACKET_LENGTH, 1) != ERR_OK)
				return E_EROR;
			else
				vTaskDelay(1000);
			tcp_sent(pcb, NULL);
			i2 += ETERNET_MAX_PACKET_LENGTH;
		}
		i = strlen((char*)&netStruct.netPacket[i2]);
	}
	vTaskDelay(1000);
	if(tcp_write(pcb, &netStruct.netPacket[i2], i, 1) != ERR_OK)
		return E_EROR;
	else
		tcp_sent(pcb, NULL);
	vTaskDelay(1000);
	return E_OK;
}

eReturn NetworkSendGSM(void)
{
	if(GSM_TCP_send() == E_OK)
		return E_OK;
	else
		return E_GSM_ERR;
}

eReturn NetworkModbusCheckBuffer(void)
{
	if(strlen((char*)&netStruct.netPacket) >= (NETPACKET_SIZE - NETPACKET_RESERV))
		return E_OK;
	else
		return E_EROR;
}

eReturn NetworkModbusAction(uint8_t type)
{
	if(type == E_NETWORK_MODBUS_UP)
	{
		netStruct.tune_action = E_NET_STATE_TUNE_CONNECT_UP;
		while(netStruct.tune_action == E_NET_STATE_TUNE_CONNECT_UP)
			vTaskDelay(10);
		if(netStruct.connect_state == E_NET_STATE_CONNECT_UP)
			return E_OK;
		else
			return E_EROR;
	}
	if(type == E_NETWORK_MODBUS_DOWN)
	{
		netStruct.tune_action = E_NET_STATE_TUNE_CONNECT_DOWN;
		while(netStruct.tune_action == E_NET_STATE_TUNE_CONNECT_DOWN)
			vTaskDelay(100);
		return E_OK;
	}
	if(type == E_NETWORK_MODBUS_SEND)
	{
		netStruct.tune_action = E_NET_STATE_SEND_PACKET;
		while(netStruct.tune_action == E_NET_STATE_SEND_PACKET)
			vTaskDelay(10);
		return E_OK;
	}
	if(type == E_NETWORK_MODBUS_SEND_END)
	{
		NetworkEndPacket();
		netStruct.tune_action = E_NET_STATE_SEND_PACKET;
		while(netStruct.tune_action == E_NET_STATE_SEND_PACKET)
			vTaskDelay(10);
		return E_OK;
	}
	return E_EROR;
}
